﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Diagnostics;

namespace SimpleListEditor
{
    public partial class ListEditorControl <T>: UserControl
    {

        private Dictionary<string, T> dListItems = null;

        public Dictionary<string, T> ListItems
        {
            get { return dListItems; }
            set { dListItems = value; }
        }
        public List<string> ProhibitedItems = new List<string>();

        public delegate void SelectedIndexChangedDelegate(object sender, EventArgs e);
        public event SelectedIndexChangedDelegate SelectedIndexChanged;

        public delegate string ParseConvert(string item);
        public ParseConvert InputToParsedKeyConverter;
        public string InputToKey_Default(string item)
        {
            return item; //>> forwarding operation
        }

        public ListEditorControl(ref Dictionary<string, T> listItemsToReference)
        {
            InitializeComponent();
            if (listItemsToReference == null)
            {
                throw new ArgumentNullException();
            }
            ListItems = listItemsToReference;
            this.InputToParsedKeyConverter = InputToKey_Default;
            ProhibitedItems.Add(null);
            ProhibitedItems.Add("");


            //groupForName = new ListViewGroup();
            //groupForName.Header = "Name";
        }

        public void AddItem(string itemTextAndKey, bool refreshInterface, bool addAutoCompleteEntry)
        {

            if (ProhibitedItems.Contains(itemTextAndKey))
            {
                //DO NOT ADD PROHIBITED ITEMS
                return;
            }

            if (!dListItems.ContainsKey(itemTextAndKey))
            {

                T listItem = CreateItemDefault(itemTextAndKey);
                dListItems.Add(itemTextAndKey, listItem);

                if (refreshInterface)
                {
                    //ListViewItem itemToAddToListView = new ListViewItem(itemTextAndKey, groupForName);
                    //itemToAddToListView.Tag = itemTextAndKey;
                    lvITEMS.Items.Add(itemTextAndKey, itemTextAndKey, 0);
                }
                if (addAutoCompleteEntry)
                {
                    AutoCompleteCollection.Add(itemTextAndKey);
                }
            }
            else
            {
                //do not add DUPLICATES
            }
        }

        protected AutoCompleteStringCollection GetNewAutoCompleteCollection
        {
            get
            {
                AutoCompleteStringCollection coll = new AutoCompleteStringCollection();
                foreach (T o in dListItems.Values)
                {
                    string sValue = o.ToString();
                    coll.Add(sValue);
                }
                return coll;
            }
        }

        private AutoCompleteStringCollection AutoCompleteCollection
        {
            get
            {
                return cbItemText.AutoCompleteCustomSource;
            }
            set
            {
                cbItemText.AutoCompleteCustomSource = value;
            }
        }

        public void RemoveItem(string itemText, bool refreshRedraw)
        {

            string parsedKey = InputToParsedKeyConverter(itemText);
            object objToRemove = FindByText(parsedKey);

            if (refreshRedraw &&
                objToRemove != null)
            {
                if (this.dListItems.ContainsKey(parsedKey))
                {
                    dListItems.Remove(parsedKey);
                    lvITEMS.Items.RemoveByKey(parsedKey);
                }
            }
        }
        
        private void lvITEMS_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView.SelectedListViewItemCollection selected = lvITEMS.SelectedItems;
            if (selected.Count > 0)
            {
                string parsedKey = this.InputToParsedKeyConverter(selected[0].Text);

                cbItemText.Text = parsedKey;

                //item may have been removed, just not updated the list
                if (this.dListItems.ContainsKey(parsedKey))
                {
                    pgEditor.SelectedObject = this.dListItems[parsedKey];
                }
            }

            if (SelectedIndexChanged != null)
                SelectedIndexChanged(sender, e);
        }
        public bool SaveToBinaryFile(string fileName)
        {
            object[] allToSave = GetDataValues();

            return SaveToBinaryFile(allToSave, fileName);
        }
        public Dictionary<string, T> GetData()
        {
            return dListItems;
        }
        public object[] GetDataValues()
        {
            object[] allToSave = new object[dListItems.Count];
            int idx = 0;
            foreach (string key in dListItems.Keys)
                allToSave[idx++] = dListItems[key];
            return allToSave;
        }

        public static bool SaveToBinaryFile(object objToSave, string fileName)
        {

            if (objToSave == null)
                return false;

            FileStream WriteFileStream = null;
            try
            {
                // Create a new XmlSerializer instance with the type of the test class
                BinaryFormatter SerializerObj = new BinaryFormatter();// XmlSerializer(objToSave.GetType());

                // Create a new file stream to write the serialized object to a file
                WriteFileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
                SerializerObj.Serialize(WriteFileStream, objToSave);
                //SerializerObj.Serialize(WriteFileStream, objToSave);
                WriteFileStream.Flush();
                // Cleanup

                return true;
            }
            catch (Exception ioException)
            {
                return false;
            }
            finally
            {
                if (WriteFileStream != null)
                    WriteFileStream.Close();
            }
        }

        private object AddByItemByText(string sUnparsedTextToAdd, bool updateUserInterface)
        {
            string inputItemKey = null; 
            
            if (this.InputToParsedKeyConverter != null)
            {
                inputItemKey = this.InputToParsedKeyConverter(sUnparsedTextToAdd);
            }
            else
            {
                 inputItemKey = sUnparsedTextToAdd;
            }

            T itemToAdd = default(T); //might not be able to null this

            if (ProhibitedItems.Contains(inputItemKey))
            {
                //DO NOT ADD PROHIBITED ITEMS
                return itemToAdd;
            }

            if (!this.dListItems.ContainsKey(inputItemKey))
            {
                itemToAdd = this.CreateItemDefault(inputItemKey);

                this.dListItems.Add(inputItemKey, itemToAdd);
                if (updateUserInterface)
                {
                    string nodeText = inputItemKey;

                    //ListViewItem li = new ListViewItem(nodeText, groupForName);
                    //li.Tag = inputItemKey;

                    lvITEMS.Items.Add(inputItemKey, nodeText, 0);
                    lvITEMS.Update();
                }
                return itemToAdd;
            }

            return itemToAdd;
        }

        //public Type ItemTypeForCreating = null;

        T CreateItemDefault(string input)
        {
            Type[] taArgTypes = new Type[] { typeof(string) };

            try
            {
                //if (ItemTypeForCreating == null)
                //{
                //    if (dListItems.Count > 0)
                //    {
                //        foreach (string k in dListItems.Keys) //get a first item
                //        {
                //            ItemTypeForCreating = dListItems[k].GetType();
                //            break;
                //        }
                //    }
                //}
                //if (ItemTypeForCreating == null)
                //{
                //    throw new Exception("No item type can be found, so we cannot create the item.");
                //}
                System.Reflection.ConstructorInfo constructor = typeof(T).GetConstructor(taArgTypes);
                if (constructor == null)
                {
                    //cannot get a constructor(string)
                    throw new Exception("Constructor (string) required for automatic item creation.");
                }
                object[] paramsArrayForConstructor = new object[] { (string)input }; //...add the string you call input to invokation parameters
                T ojbResult = (T)constructor.Invoke(paramsArrayForConstructor);
                return (T)ojbResult;
            }
            catch (Exception constructorError)
            {
                throw constructorError;
            }
        }

        private void btnRemoveByText_Click(object sender, EventArgs e)
        {
            //remove from items and not from text (so they can type it back in again)
            string parsedKey = this.InputToParsedKeyConverter(cbItemText.Text);
            ListViewItem itemRemoved= RemoveItemByKey(parsedKey, true);
            if (pgEditor.SelectedObject == itemRemoved)
            {
                pgEditor.SelectedObject = null;
            }
            //lvITEMS.Refresh();
        }

        private ListViewItem RemoveItemByKey(string parsedKey, bool listViewRefresh)
        {
            ListViewItem found = null;
            if (this.dListItems.ContainsKey(parsedKey))
            {
                dListItems.Remove(parsedKey);
                if (listViewRefresh)
                {
                    //ListViewItem liCopyOfGuyToRemove = new ListViewItem(parsedKey, groupForName);
                    //lvITEMS.Items.Remove(

                    found = FindByText(parsedKey);
                    if (found != null)
                    {
                        lvITEMS.Items.Remove(found);
                    }

                    lvITEMS.Update();
                }
            }
            return found;
        }

        //private void RemoveByTag(string parsedKey)
        //{
        //    ListViewItem found = FindByTag(parsedKey);
        //    if (found != null)
        //    {
        //        lvITEMS.Items.Remove(found);
        //    }
        //}
        private ListViewItem FindByText(string parsedKey)
        {
            foreach (ListViewItem lvi in lvITEMS.Items)
            {
                if (lvi.Text == parsedKey)
                {
                    return lvi;
                }
            }
            return null;
        }
        //private ListViewItem FindByTag(string parsedKey)
        //{
        //    foreach (ListViewItem lvi in lvITEMS.Items)
        //    {
        //        if (lvi.Tag != null && lvi.Tag.ToString() == parsedKey)
        //        {
        //            return lvi;
        //        }
        //    }
        //    return null;
        //}

        private void cbItemText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                //AddByItemByText(cbItemText.Text, true);
                ListViewItem lvToSelect = FindByText(cbItemText.Text);
                if (lvToSelect != null)
                    lvToSelect.Selected = true;
            }
        }


        public string SelectedKey
        {
            get
            {
                System.Windows.Forms.ListView.SelectedListViewItemCollection selectedItems = this.lvITEMS.SelectedItems;
                if (selectedItems == null || selectedItems.Count == 0)
                    return null;
                string selKey = selectedItems[0].Tag.ToString();
                return selKey;
            }
        }
        public System.Windows.Forms.ListView.SelectedListViewItemCollection SelectedItems
        {
            get
            {
                System.Windows.Forms.ListView.SelectedListViewItemCollection selectedItems = this.lvITEMS.SelectedItems;
                if (selectedItems == null || selectedItems.Count == 0)
                    return null;
                return selectedItems;
            }
        }

        public static object LoadObjectFromBinaryFile(string file)
        {

            try
            {
                // Create a new file stream for reading the XML file
                if (File.Exists(file))
                {
                    FileStream ReadFileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                    // Create a new XmlSerializer instance with the type of the test class
                    BinaryFormatter bf = new BinaryFormatter();
                    //XmlSerializer SerializerObj = new XmlSerializer(typeof(List<string>));

                    // Load the object saved above by using the Deserialize function
                    object objLoaded = bf.Deserialize(ReadFileStream);//SerializerObj.Deserialize(ReadFileStream);

                    // Cleanup
                    ReadFileStream.Close();
                    return objLoaded;
                }
                else return null;

            }
            catch (Exception e)
            {
                return null;
            }
        }
        public void LoadFromBinaryFile(string file, bool doControlUpdates)
        {
            object[] all = (object[])LoadObjectFromBinaryFile(file);

            //dListItems = new Dictionary<string, T>();

            dListItems.Clear();
            
            if (all == null)
                return;
            else
            {
                foreach (T obj_toAdd in all)
                {
                    string key = obj_toAdd.ToString();
                    dListItems.Add(key, obj_toAdd);
                    if (doControlUpdates)
                    {
                        this.lvITEMS.Items.Add(key, key, 0);
                    }
                }
            }
            //var dic2 = dListItems.ToDictionary(kvp => kvp.Key as string, kvp => (T)kvp.Value);

            //dListItems = dic2;
        }
        //public void LoadFromXMLFile(string file)
        //{
        //    throw new NotImplementedException();
        //}

        public static object LoadObjectFromXMLFile(string file)
        {

            try
            {
                // Create a new file stream for reading the XML file
                if (File.Exists(file))
                {
                    FileStream ReadFileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                    // Create a new XmlSerializer instance with the type of the test class
                    XmlSerializer SerializerObj = new XmlSerializer(typeof(List<string>));

                    // Load the object saved above by using the Deserialize function
                    object objLoaded = SerializerObj.Deserialize(ReadFileStream);

                    // Cleanup
                    ReadFileStream.Close();
                    return objLoaded;
                }
                else return null;

            }
            catch (Exception e)
            {
                return null;
            }
        }
        public override string Text
        {
            get
            {
                return cbItemText.Text;
            }
            set { cbItemText.Text = value; }
        }
        public string ButtonCaption
        {
            get
            {
                return btnSaveTo.Text;
            }
            set
            {
                btnSaveTo.Text = value;
            }
        }
        private void btnSaveTo_Click(object sender, EventArgs e)
        {
            Save_ContextMenuStrip.Show(btnSaveTo, new Point(0, 0));
        }

        private void copyAsPlainTextToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CopyAllItemsToClipboardAs(TextDataFormat.Text);

        }

        private void CopyAllItemsToClipboardAs(TextDataFormat textFormat)
        {
            StringBuilder sb = new StringBuilder("");
            foreach (string key in this.dListItems.Keys)
            {
                object data = dListItems[key];
                string sMemData = data.ToString();
                sb.AppendLine(sMemData);
            }
            string entireListString = sb.ToString();
            if (entireListString != null && entireListString != "")
                Clipboard.SetText(entireListString, textFormat);
            else
                Debug.WriteLine("Could not copy to clipboard. No data in list.");
        }

        private void lvITEMS_Click(object sender, EventArgs e)
        {
            RefreshItems();
        }
        public bool Panel2Collapsed
        {
            get {
                return scList_PropertyGrid.Panel2Collapsed;
            }
            set
            {
                scList_PropertyGrid.Panel2Collapsed = value;
            }
        }
        public bool Panel1Collapsed
        {
            get
            {
                return scList_PropertyGrid.Panel1Collapsed;
            }
            set
            {
                scList_PropertyGrid.Panel1Collapsed = value;
            }
        }
        public void RefreshItems()
        {
            //check for nodes to remove
            foreach (ListViewItem itemToCrossRef in lvITEMS.Items)
            {
                string key = itemToCrossRef.Text;
                if (!dListItems.ContainsKey(key))
                {
                    lvITEMS.Items.Remove(itemToCrossRef);
                }
            }
            foreach (string key in dListItems.Keys)
            {
                //ListViewItem newItem = new ListViewItem(key);
                if (key != null && key != "")
                {
                    if (!lvITEMS.Items.ContainsKey(key))
                    {
                        lvITEMS.Items.Add(key, key, 0);
                    }
                }
            }
            lvITEMS.Refresh ();
        }

        private void cbItemText_Click(object sender, EventArgs e)
        {
            AutoCompleteCollection = this.GetNewAutoCompleteCollection;
        }

        private void btnAddItem_Click_1(object sender, EventArgs e)
        {
            if (cbItemText.Text == "")
            {
                //CANNOT ADD "" STRING BY ANY MEANS
                return; //
            }
            AddByItemByText(cbItemText.Text, true);

        }

        private void cbItemText_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListViewItem lviFromText = FindByText(cbItemText.Text);
            if (lviFromText != null)
            {
                lviFromText.Selected = true;
                lvITEMS_SelectedIndexChanged(sender, e);
            }
        }

    }//end of class
} //end of current namepsace
